home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Plus Special 17
/
AMIGAplus Sonderheft 17 (1999)(ICP)(DE)[!].iso
/
PD
/
Anwendungen
/
-DataTypes-
/
mpegaudio
/
dispatch.c
< prev
next >
Wrap
C/C++ Source or Header
|
1998-06-17
|
20KB
|
619 lines
/*
**
** $VER: dispatch.c 2.2 (1.5.98)
** mpegaudio.datatype 2.2
**
** Dispatch routine for a DataTypes class
**
** Written 1996-1998 by Roland 'Gizzy' Mainz
** Original example source from David N. Junod
**
*/
/* main includes */
#include "classbase.h"
#include "classdata.h"
/*****************************************************************************/
/* local prototypes */
static DISPATCHERFLAGS ULONG Dispatch( REGA0 struct IClass *, REGA2 Object *, REGA1 Msg );
static BOOL LoadSample( struct ClassBase *, Object * );
#ifndef NO_ENCODER
static ULONG SaveMPEGAudio( struct ClassBase *, Object *, struct dtWrite * );
#endif /* NO_ENCODER */
static DISPATCHERFLAGS ULONG bitstream_hook( REGA0 struct Hook *, REGA2 APTR , REGA1 MPEGA_ACCESS * );
/*****************************************************************************/
/* Create "mpegaudio.datatype" BOOPSI class */
struct IClass *initClass( struct ClassBase *cb )
{
struct IClass *cl;
/* Create our class... */
if( cl = MakeClass( MPEGAUDIODTCLASS, SOUNDDTCLASS, NULL, (ULONG)sizeof( struct MPEGAudioInstData ), 0UL ) )
{
#define DTSTACKSIZE (16384UL)
cl -> cl_Dispatcher . h_Entry = (HOOKFUNC)StackSwapDispatch; /* see stackswap.c */
cl -> cl_Dispatcher . h_SubEntry = (HOOKFUNC)Dispatch; /* see stackswap.c */
cl -> cl_Dispatcher . h_Data = (APTR)DTSTACKSIZE; /* see stackswap.c */
cl -> cl_UserData = (ULONG)cb; /* class library base as expected by datatypes.library */
AddClass( cl );
}
return( cl );
}
/*****************************************************************************/
/* class dispatcher */
static
DISPATCHERFLAGS
ULONG Dispatch( REGA0 struct IClass *cl, REGA2 Object *o, REGA1 Msg msg )
{
struct ClassBase *cb = (struct ClassBase *)(cl -> cl_UserData);
ULONG retval = 0UL;
switch( msg -> MethodID )
{
/****** mpegaudio.datatype/OM_NEW ********************************************
*
* NAME
* OM_NEW -- Create a mpegaudio.datatype object.
*
* FUNCTION
* The OM_NEW method is used to create an instance of the
* mpegaudio.datatype class. This method is passed to the superclass
* first. After this, mpegaudio.datatype loads the sample using
* "mpega.library"'s decoder into memory.
*
* ATTRIBUTES
* The following attributes can be specified at creation time.
*
* DTA_SourceType (ULONG) -- Determinates the type of DTA_Handle
* attribute. Only DTST_FILE is supported.
* If any other type was set in a given DTA_SourceType,
* OM_NEW will be rejected.
* Defaults to DTST_FILE.
*
* DTA_Handle -- For DTST_FILE, a BPTR to a lock is expected by
* datatypesclass, which will convert this into a filehandle.
* A DTST_RAM (create empty object) source type requires a NULL
* handle.
*
* NOTE
* If the datatype was compiled with the NO_ENCODER flag set,
* DTA_SourceType == DTST_RAM causes OM_NEW to reject the method.
*
* RESULT
* If the object was created a pointer to the object is returned,
* otherwise NULL is returned.
*
******************************************************************************
*
*/
case OM_NEW:
{
struct TagItem *ti;
/* We only support DTST_FILE or DTST_RAM as source type */
if( ti = FindTagItem( DTA_SourceType, (((struct opSet *)msg) -> ops_AttrList) ) )
{
if( ((ti -> ti_Data) != DTST_FILE)
#ifndef NO_ENCODER
&& ((ti -> ti_Data) != DTST_RAM)
#endif /* !NO_ENCODER */
)
{
SetIoErr( ERROR_OBJECT_WRONG_TYPE );
break;
}
}
/* This must not be a subclass of mpegaudio.datatype
* (not implemented yet)
*/
if( o == (Object *)cl )
{
if( retval = DoSuperMethodA( cl, o, msg ) )
{
/* Load sample... */
if( !LoadSample( cb, (Object *)retval ) )
{
/* Something went fatally wrong, dispose object */
CoerceMethod( cl, (Object *)retval, OM_DISPOSE );
retval = 0UL;
}
}
}
else
{
/* Subclasses of mpegaudio.datatype are not implemented */
SetIoErr( ERROR_NOT_IMPLEMENTED );
}
}
break;
/****** mpegaudio.datatype/OM_DISPOSE ****************************************
*
* NAME
* OM_DISPOSE -- Delete object
*
* FUNCTION
* Frees the contents of the mpegvideo.datatype instance data
* and passes then the msg to the superclass.
*
* RESULT
* Returns 0 evertimes.
*
******************************************************************************
*
*/
case OM_DISPOSE:
{
LONG saved_ioerr = IoErr(); /* save I/O error */
/* Free ourselves */
DoSuperMethodA( cl, o, msg );
/* Restore I/O error */
SetIoErr( saved_ioerr );
}
break;
case OM_UPDATE:
{
if( DoMethod( o, ICM_CHECKLOOP ) )
{
break;
}
}
case OM_SET:
{
/* Pass the attributes to the sound class and force a refresh if we need it */
if( retval = DoSuperMethodA( cl, o, msg ) )
{
/* The following check statement isn't needed because OM_NEW does not allow subclasses of mpegaudio.datatype,
* therefore, we're always the top instance...
*/
#ifdef COMMENTED_OUT
/* Top instance ? */
if( OCLASS( o ) == cl )
#endif /* COMMENTED_OUT */
{
struct RastPort *rp;
/* Get a pointer to the rastport */
if( rp = ObtainGIRPort( (((struct opSet *)msg) -> ops_GInfo) ) )
{
struct gpRender gpr;
/* Force a redraw */
gpr . MethodID = GM_RENDER;
gpr . gpr_GInfo = ((struct opSet *)msg) -> ops_GInfo;
gpr . gpr_RPort = rp;
gpr . gpr_Redraw = GREDRAW_UPDATE;
DoMethodA( o, (Msg)(&gpr) );
/* Release the temporary rastport */
ReleaseGIRPort( rp );
/* We did a refresh... */
retval = 0UL;
}
}
}
}
break;
/****** mpegaudio.datatype/DTM_WRITE **********************************************
*
* NAME
* DTM_WRITE -- Save data
*
* FUNCTION
* This method saves the object's contents to disk.
*
* If dtw_Mode is DTWM_IFF, the method is passed unchanged to the
* superclass, sound.datatype, which writes an IFF 8SVX sample.
*
* If dtw_mode is DTWM_RAW, the object writes a MPEG audio stream to
* the filehandle given.
* (If the class library was compiled with the NO_ENCODER switch
* (not the default), result == 0 and resul2 == ERROR_NOT_IMPLEMENTED
* are returned).
*
* TAGS
* None defined.
*
* RESULT
* Returns 0 for failure (IoErr() returns result2), non-zero
* for success.
*
******************************************************************************
*
*/
case DTM_WRITE:
{
struct dtWrite *dtw;
dtw = (struct dtWrite *)msg;
/* Local data format requested ? */
if( (dtw -> dtw_Mode) == DTWM_RAW )
{
/* Enable the followng code if you don't have an encoder implemented... */
#ifdef NO_ENCODER
SetIoErr( ERROR_NOT_IMPLEMENTED );
retval = 0UL;
#else
retval = SaveMPEGAudio( cb, o, dtw );
#endif /* NO_ENCODER */
}
else
{
/* Pass msg to superclass (which writes an IFF 8SVX sample)... */
retval = DoSuperMethodA( cl, o, msg );
}
}
break;
/* Let the superclass handle everything else */
default:
{
retval = DoSuperMethodA( cl, o, msg );
}
break;
}
return( retval );
}
static
BOOL LoadSample( struct ClassBase *cb, Object *o )
{
struct MPEGAudioInstData *maid = (struct MPEGAudioInstData *)INST_DATA( (cb -> cb_Lib . cl_Class), o );
LONG error = 0L;
BOOL success = FALSE;
BPTR fh; /* stream handle */
ULONG sourcetype; /* type of stream (either DTST_FILE or DTST_RAM */
struct VoiceHeader *vh; /* obj's voice header */
/* Get file handle, handle type and VoiceHeader */
if( GetDTAttrs( o, DTA_SourceType, (&sourcetype),
DTA_Handle, (&fh),
SDTA_VoiceHeader, (&vh),
TAG_DONE ) == 3UL )
{
switch( sourcetype )
{
case DTST_FILE:
{
/* We got all what we want (a filehandle) */
}
break;
#ifndef NO_ENCODER
case DTST_RAM:
{
/* Do nothing... */
}
break;
#endif /* !NO_ENCODER */
default:
{
/* unsupported source type */
error = ERROR_NOT_IMPLEMENTED;
}
break;
}
/* Any error ? */
if( error == 0L )
{
if( fh )
{
BYTE *sample = NULL;
LONG pcm_pos = 0L; /* position in buffer */
struct Hook accesshook = { 0 };
MPEGA_CTRL mpa_ctrl =
{
NULL, // Bitstream access hook, see below (set to (&accesshook))
// Layers I & II settings (mono, stereo)
{ FALSE, { 2, 0, 20000 }, { 2, 0, 20000 } },
// Layer III settings (mono, stereo)
{ FALSE, { 2, 0, 20000 }, { 2, 0, 20000 } },
0, // Don't check mpeg validity at start (needed for mux stream)
1024 // Stream Buffer size
};
ULONG samplesize = 0UL;
/* init hook */
accesshook . h_Entry = (HOOKFUNC)bitstream_hook;
accesshook . h_SubEntry = (HOOKFUNC)cb; /* misused here as 2nd data field... */
accesshook . h_Data = (APTR)fh;
mpa_ctrl . bs_access = (&accesshook);
if( maid -> maid_MPAS = MPEGA_open( NULL, (&mpa_ctrl) ) )
{
WORD left[ MPEGA_PCM_SIZE + 32 ],
right[ MPEGA_PCM_SIZE + 32 ],
*pcm[ 2 ];
LONG pcm_count;
pcm[ 0 ] = left;
pcm[ 1 ] = right;
/* Decoder frames into a continous ram buffer; if the buffer gets to small, enlarge it... */
while( (pcm_count = MPEGA_decode_frame( (maid -> maid_MPAS), pcm )) >= 0 )
{
BYTE *newsample;
ULONG next = pcm_pos + pcm_count + 32;
if( samplesize <= next )
{
next += MPEGA_PCM_SIZE * 256UL;
if( newsample = (BYTE *)AllocVec( (next + 256), MEMF_PUBLIC ) )
{
/* "sample" and "newsample" are long-word aligned, and "pcm_pos" can be padded without trouble,
* therefore I can use CopyMemQuick here...
*/
CopyMemQuick( sample, newsample, ((pcm_pos + 3UL) & ~3UL) );
FreeVec( sample );
sample = newsample;
samplesize = next;
}
else
{
/* no buffer memory */
pcm_count = MPEGA_ERR_MEM;
break;
}
}
#ifdef COMNTED_OUT
/* KISS PCM->sample conversion function */
{
LONG i;
/* Stereo ? */
if( (maid -> maid_MPAS -> channels) == 2 )
{
/* Convert left+right channels */
for( i = 0L ; i < pcm_count ; i++ )
{
sample[ i + pcm_pos ] = ((left[ i ] + right[ i ]) >> 9); /* ((left + right) / 2) >> 8 */
}
}
else
{
/* Convert left channel (mono) only */
for( i = 0L ; i < pcm_count ; i++ )
{
sample[ i + pcm_pos ] = (left[ i ] >> 8); /* ((left + right) / 2) >> 8 */
}
}
}
#else
/* This replacement for the loop above is much faster because
* the ratio "loop-check vs. PCM->sample-conversion" is ~~ 1:8
* instead of 1:1 (like above)...
*/
{
/* Stereo ? */
if( (maid -> maid_MPAS -> channels) == 2 )
{
/* Convert left+right channels */
register ULONG blocks = (pcm_count & ~(0x07UL)) >> 3UL,
cut = pcm_count & (0x07UL),
i;
register BYTE *sptr = sample + pcm_pos;
register WORD *lbptr = left,
*rbptr = right;
/* Copy sample blocks... */
for( i = 0UL ; i < blocks ; i++ )
{
*sptr++ = ((*lbptr++ + *rbptr++) >> 9);
*sptr++ = ((*lbptr++ + *rbptr++) >> 9);
*sptr++ = ((*lbptr++ + *rbptr++) >> 9);
*sptr++ = ((*lbptr++ + *rbptr++) >> 9);
*sptr++ = ((*lbptr++ + *rbptr++) >> 9);
*sptr++ = ((*lbptr++ + *rbptr++) >> 9);
*sptr++ = ((*lbptr++ + *rbptr++) >> 9);
*sptr++ = ((*lbptr++ + *rbptr++) >> 9);
}
/* ... then the remaining samples. */
for( i = 0UL ; i < cut ; i++ )
{
*sptr++ = ((*lbptr++ + *rbptr++) >> 9);
}
}
else
{
/* Convert left channel (mono) only */
register ULONG blocks = (pcm_count & ~(0x07UL)) >> 3UL,
cut = pcm_count & (0x07UL),
i;
register BYTE *sptr = sample + pcm_pos;
register WORD *lbptr = left;
/* Copy sample blocks... */
for( i = 0UL ; i < blocks ; i++ )
{
*sptr++ = (*lbptr++ >> 8);
*sptr++ = (*lbptr++ >> 8);
*sptr++ = (*lbptr++ >> 8);
*sptr++ = (*lbptr++ >> 8);
*sptr++ = (*lbptr++ >> 8);
*sptr++ = (*lbptr++ >> 8);
*sptr++ = (*lbptr++ >> 8);
*sptr++ = (*lbptr++ >> 8);
}
/* ... then the remaining samples. */
for( i = 0UL ; i < cut ; i++ )
{
*sptr++ = (*lbptr++ >> 8);
}
}
}
#endif /* COMMENTED_OUT */
pcm_pos += pcm_count;
}
/* dispatch error code... */
switch( pcm_count )
{
case MPEGA_ERR_NONE: break;
case MPEGA_ERR_EOF: break;
case MPEGA_ERR_BADFRAME: error = DTERROR_INVALID_DATA; break;
case MPEGA_ERR_MEM: error = ERROR_NO_FREE_STORE; break;
case MPEGA_ERR_NO_SYNC: error = DTERROR_INVALID_DATA; break;
}
/* No error ? */
if( !error )
{
ULONG clock = ((SysBase -> ex_EClockFrequency) * 5UL); /* amiga clock */
ULONG period = clock / (maid -> maid_MPAS -> dec_frequency);
/* Set up voice header */
vh -> vh_OneShotHiSamples = clock / period;
vh -> vh_RepeatHiSamples = 0UL;
vh -> vh_SamplesPerHiCycle = 0UL;
vh -> vh_SamplesPerSec = clock / period;
vh -> vh_Octaves = 1UL;
vh -> vh_Compression = CMP_NONE;
vh -> vh_Volume = 0x10000UL; /* maximum volume */
/* Set misc attributes
* In fact, SDTA_Period and SDTA_Volume are calculated from SDTA_VoiceHeader info,
* but we set it here EXPLICITLY that noone can say we didn't pass this to sound.datatype...
*/
SetDTAttrs( o, NULL, NULL, SDTA_Sample, sample,
SDTA_SampleLength, pcm_pos,
SDTA_Period, period,
TAG_DONE );
sample = NULL; /* The object now owns the sample data */
/* All done... */
success = TRUE;
}
MPEGA_close( (maid -> maid_MPAS) );
}
FreeVec( sample );
}
else
{
/* no filehandle */
error = ERROR_NO_FREE_STORE;
}
}
}
else
{
/* can't get required attributes from superclass */
error = ERROR_OBJECT_WRONG_TYPE;
}
SetIoErr( error );
return( success );
}
#ifndef NO_ENCODER
/* The MPEG Audio encoder */
static
ULONG SaveMPEGAudio( struct ClassBase *cb, Object *o, struct dtWrite *dtw )
{
ULONG retval = 0UL;
LONG error = ERROR_NOT_IMPLEMENTED;
#if 0
/* A NULL file handle is a nop (GMultiView uses this to test if a datatype supports RAW writing) */
if( dtw -> dtw_FileHandle )
{
}
#endif
/* Store Result2 */
SetIoErr( error );
return( retval );
}
#endif /* !NO_ENCODER */
/* mpega.library bitstream hook
* Required because mpega.library has no way to access an already open filehandle and
* because we're not allowed to close a filehandle which is owned by "datatypesclass".
*/
static
DISPATCHERFLAGS
ULONG bitstream_hook( REGA0 struct Hook *hook, REGA2 APTR handle, REGA1 MPEGA_ACCESS *access )
{
struct ClassBase *cb = (struct ClassBase *)(hook -> h_SubEntry);
ULONG retval = 0UL;
switch( access -> func )
{
case MPEGA_BSFUNC_OPEN:
{
/* We don't know the total size :-( */
access -> data . open . stream_size = 0L;
/* Return handle */
retval = (ULONG)(hook -> h_Data);
}
break;
case MPEGA_BSFUNC_CLOSE: /* NOP */
break;
case MPEGA_BSFUNC_READ:
{
/* Check valid handle */
if( handle )
{
retval = Read( (BPTR)handle, (access -> data . read . buffer), (access -> data . read . num_bytes) );
}
}
break;
case MPEGA_BSFUNC_SEEK:
{
if( handle )
{
retval = (Seek( (BPTR)handle, (access -> data . seek . abs_byte_seek_pos), OFFSET_BEGINNING ) == -1L);
}
}
break;
}
return( retval );
}